#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include "dbus-monitor.h"

#define INDENT 4
#define MAX_RULE (DBM_MAX_RULE+16)

static void print_message(DBusMessage *message);
static void walk_iter (DBusMessageIter *iter, int depth);
static void print_iter (DBusMessageIter *iter, int depth);    
static void print_ay(DBusMessageIter *iter, int depth);
static void print_hex(const unsigned char*, unsigned, int);
static void print_fd (int fd, int depth);
static void InitData(DBusMonitor *m);
static void * startDispatch(void *data);

static const char * const msg_type2str[] = {
    "", //padding
    "method call", //DBUS_MESSAGE_TYPE_METHOD_CAL
    "methdo return", //DBUS_MESSAGE_TYPE_METHOD_RETURN
    "error", //DBUS_MESSAGE_TYPE_ERROR
    "signal" //DBUS_MESSAGE_TYPE_SIGNAL
};

struct _DBusMonitor{
    DBusConnection *m_conn;
    DBusBusType m_type;
    char *m_addr;
    int m_ctlfd[2];  //control fd
    DBusMonitorExecptionHandler m_eh; //execption handler
    void *m_ehd; //execption handler user data
    DBusWatch *m_dws[DBM_MAX_WATCHES]; //DBus Watches
    int m_wc; //DBus watches count
    DBusAddWatchFunction m_awf; //add watch function
    DBusRemoveWatchFunction m_rwf; //remove watch function
    DBusMonitorDispatchFunc m_df; // dispatch function
    pthread_t m_tid; // DBusMonitor thread id
};

void *startDispatch(void *data) {
    DBusMonitor *m = data;
    m->m_df(m);
    return NULL;
}

int dbmGetWFd(DBusMonitor *m, int i) {
    if(i>=m->m_wc) return -1;
    return dbus_watch_get_unix_fd(m->m_dws[i]);   
}

void dbmSetExecptionHandler(DBusMonitor *m,
                                   DBusMonitorExecptionHandler eh, void *usr_data) {
    m->m_eh = eh;
    m->m_ehd = usr_data;
}

DBusWatch *dbmGetDBW(DBusMonitor *m, int fd) {
    int i;
    for(i=0; i<m->m_wc; i++) {
        if(dbus_watch_get_unix_fd(m->m_dws[i]) == fd) {
            return m->m_dws[i];
        }
    }
    return NULL;
}
dbus_bool_t dbmAddDBW(DBusMonitor *m, DBusWatch *w) {
    if(m->m_wc < DBM_MAX_WATCHES) {
        m->m_dws[m->m_wc++] = w;
        return TRUE;
    }
    return FALSE;
}

inline void InitData(DBusMonitor *m) {
    m->m_eh = NULL;
    m->m_ehd = NULL;
    m->m_wc = 0;
    m->m_ctlfd[0] = m->m_ctlfd[1] = -1;
}

int dbmGetCtrlFd(DBusMonitor *m) {
    return m->m_ctlfd[0];
}

dbus_bool_t dbmInitConnection(DBusMonitor *m) {
    const char *typestr = NULL;
    DBusError err = DBUS_ERROR_INIT;
    if(pipe(m->m_ctlfd) != 0) return FALSE;
    if(m->m_addr == NULL) {
        switch (m->m_type) {
        case DBUS_BUS_SYSTEM:
            typestr = "system";
            break;
        case DBUS_BUS_SESSION:
            typestr = "session";
            break;
        default:
            fprintf(stderr, "Unspported bus type: %d\n", m->m_type);
            return FALSE;
        }
        printf("calling dbus_bus_get_private\n");
        m->m_conn = dbus_bus_get_private(m->m_type, &err);
        if(m->m_conn == NULL) {
            fprintf(stderr, "Failed to connect to %s bus: %s: %s\n",
                            typestr, err.name, err.message);

            dbus_error_free(&err);
            return FALSE;
        }
    }else {
        printf("calling dbus_connection_open_private\n");
        m->m_conn = dbus_connection_open_private(m->m_addr, &err);
        if(m->m_conn == NULL) {
            fprintf(stderr, "Failed to open connection to bus: %s: %s\n",
                            err.name, err.message);
            dbus_error_free(&err);
            return FALSE;
        } else if(!dbus_bus_register (m->m_conn, &err)) {
            fprintf(stderr, "Failed to register connection to bus: %s: %s\n",
                            err.name, err.message);
            dbus_error_free(&err);
            return FALSE;
        }

    }
    
    dbus_connection_set_route_peer_messages (m->m_conn, TRUE);
    dbus_connection_set_exit_on_disconnect(m->m_conn, FALSE);
    printf("calling dbus_connection_set_watch_functions\n");
    if (!dbus_connection_set_watch_functions(m->m_conn, m->m_awf, m->m_rwf,
                                              NULL, m, NULL)) {
         fprintf(stderr, "dbus_connection_set_watch_functions() failed");
         return FALSE;

     }
    return TRUE;
}



DBusMonitor *dbmCreateWithType(DBusBusType type, DBusAddWatchFunction awf,
                               DBusRemoveWatchFunction rwf, 
                               DBusMonitorDispatchFunc df) {
    DBusMonitor *m = malloc(sizeof(DBusMonitor));
    if(m == NULL) return NULL;
    m->m_type = type;
    m->m_addr = NULL;
    m->m_awf = awf;
    m->m_rwf = rwf;
    m->m_df = df; 
    InitData(m);
    return m;
}

DBusMonitor *dbmCreateWithAddr(const char *addr, DBusAddWatchFunction awf,
                               DBusRemoveWatchFunction rwf, 
                               DBusMonitorDispatchFunc df) {
    DBusMonitor *m = malloc(sizeof(DBusMonitor));   
    if(m == NULL) return NULL;
    InitData(m);                   
    m->m_awf = awf;
    m->m_rwf = rwf;
    m->m_df = df;
    m->m_addr = strndup(addr, DBM_MAX_ADDR);
    if(m->m_addr == NULL) {
        dbmDestroy(m);
        return NULL;
    }
    m->m_type = -1;
    return m;
}



//如果Start成功则只能在Stop完成后调用
void dbmDestroy(DBusMonitor *m) {
    if(m==NULL) return;
    if(m->m_addr) free(m->m_addr);
    if(m->m_ctlfd[0]>=0) close(m->m_ctlfd[0]);
    if(m->m_ctlfd[1]>=0) close(m->m_ctlfd[1]);
    free(m);
}


void dbmProcessDBusEvent(DBusMonitor *m, DBusWatch *w, unsigned flags) {
    printf("calling dbus_watch_handle\n");
    while (!dbus_watch_handle(w, flags)) {
            fprintf(stderr, "dbus_watch_handle needs more memory\n");
            sleep(1);
    }
    dbus_connection_ref(m->m_conn);
    while (dbus_connection_get_dispatch_status(m->m_conn) == 
           DBUS_DISPATCH_DATA_REMAINS)
        dbus_connection_dispatch(m->m_conn);
    dbus_connection_unref(m->m_conn);
}

void dbmHandleDBusRead(DBusMonitor *m, int fd) {
    DBusWatch *w = dbmGetDBW(m, fd);
    dbmProcessDBusEvent(m, w, DBUS_WATCH_READABLE);
}
void dbmHandleDBusWrite(DBusMonitor *m, int fd) {
    fprintf(stderr, "function:%s, line:%d, file:%s\n", __func__, __LINE__, __FILE__);
    DBusWatch *w = dbmGetDBW(m, fd);
    dbmProcessDBusEvent(m, w, DBUS_WATCH_WRITABLE);
}

void dbmHandleDBusExecption(DBusMonitor *m, int fd) {
    fprintf(stderr, "function:%s, line:%d, file:%s\n", __func__, __LINE__, __FILE__);
    DBusWatch *w = dbmGetDBW(m, fd);
    dbmProcessDBusEvent(m, w, DBUS_WATCH_ERROR);
    if(m->m_eh) {
        m->m_eh(m->m_ehd);
    }
}

dbus_bool_t dbmStart (DBusMonitor *m) {
    if((errno=pthread_create(&m->m_tid, NULL, startDispatch, m)) != 0) {
        perror("Create DBusMonitor thread faild");
        return FALSE;
    }
    return TRUE;
}


dbus_bool_t dbmAddRule(DBusMonitor *m, const char *rule) {
    DBusError err = DBUS_ERROR_INIT;
    dbus_bus_add_match(m->m_conn, rule, &err);
    if(dbus_error_is_set(&err)) {
        fprintf(stderr, "Failed to add rule: %s: %s\n", err.name, err.message);
        return FALSE;
    }
    return TRUE;
}

dbus_bool_t dbmRemoveRule(DBusMonitor *m, const char *rule) {
    DBusError err = DBUS_ERROR_INIT;
    dbus_bus_remove_match(m->m_conn, rule, &err);
    if(dbus_error_is_set(&err)) {
        fprintf(stderr, "Failed to remove rule: %s: %s\n", err.name, err.message);
        return FALSE;
    }
    return TRUE;

}


dbus_bool_t dbmAddRules(DBusMonitor *m, const char *rules[]) {
    static char rule[MAX_RULE];
    char *rp = rule;
    DBusError error = DBUS_ERROR_INIT;
    DBusMessage *msg;
    DBusMessage *r = NULL;
    int i;
    dbus_uint32_t zero = 0;
    DBusMessageIter appender, array_appender;
    msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
                                      DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor");

    if (msg == NULL) {
        fprintf(stderr, "OOM while %s\n", "becoming a monitor");
        goto OUT;
    }

    dbus_message_iter_init_append (msg, &appender);
    if (!dbus_message_iter_open_container (&appender, DBUS_TYPE_ARRAY, "s",
                                           &array_appender)) {
        fprintf(stderr, "OOM while %s\n", "opening string array");
        goto OUT;
    }

    for (i = 0; rules[i]!=NULL; i++) {
        snprintf(rp, MAX_RULE, "eavesdrop=true, %s", rules[i]);
        if (!dbus_message_iter_append_basic (&array_appender, DBUS_TYPE_STRING,
                                             &rp)) {
            fprintf(stderr, "OOM while %s\n", "adding rule to array");
            goto OUT;
        }
    }

    if (!dbus_message_iter_close_container (&appender, &array_appender) ||
            !dbus_message_iter_append_basic (&appender, DBUS_TYPE_UINT32, &zero)) {

        fprintf(stderr, "OOM while %s\n", "finishing arguments");
        goto OUT;
    }

    r = dbus_connection_send_with_reply_and_block (m->m_conn, msg, -1, &error);

    if (r == NULL) {
        fprintf (stderr, "dbus-monitor: failed to call new-style monitoring: "
                         "%s: \"%s\".\n",
                 error.name, error.message);
        dbus_error_free (&error);
    } else {
        dbus_message_unref (r);
    }
    OUT:
    if(msg) {
        dbus_message_unref (msg);
    }
    return (r != NULL);

}


dbus_bool_t dbmAddHandler(DBusMonitor *m,
                          DBusHandleMessageFunction handler,
                          void *usr_data,
                          DBusFreeFunction fdf) {
    return dbus_connection_add_filter(m->m_conn, handler, usr_data, fdf);
}


void dbmRemoveHandler(DBusMonitor *m, DBusHandleMessageFunction handler, 
                      void *usr_data) {
    dbus_connection_remove_filter(m->m_conn, handler, usr_data);
}

void dbmStop(DBusMonitor *m) {
    DBMCtrl stop = DBMSTOP;
    write(m->m_ctlfd[1], &stop, sizeof(DBMCtrl));
    pthread_join(m->m_tid, NULL);
}

dbus_bool_t dbmRemoveDBW(DBusMonitor *m, DBusWatch *w) {
    int i;
    for(i=0; i<m->m_wc; i++) {
        if(w == m->m_dws[i]) {
            m->m_dws[i] = m->m_dws[--m->m_wc];
            return TRUE;
        }
    }
    return FALSE;
}

void dbmCloseConnection(DBusMonitor *m) {
    if(m->m_conn) {
        dbus_connection_flush(m->m_conn);
        dbus_connection_close(m->m_conn);
        dbus_connection_unref(m->m_conn);
        m->m_conn = NULL;
    }
}



DBusHandlerResult dbmDefaultHandler(DBusConnection *connection,
                                    DBusMessage *message, void *usr_data) {
    print_message(message);
    if (dbus_message_is_signal (message,
                                DBUS_INTERFACE_LOCAL,
                                "Disconnected")) {
        DBusMonitor *m = (DBusMonitor *)usr_data;
        fprintf(stderr, "Dbus Disconnected!");
        if(m && m->m_eh) m->m_eh(m->m_ehd);

    }
    /* Monitors must not allow libdbus to reply to messages, so we eat
     * the message. See bug 1719.
     */
    return DBUS_HANDLER_RESULT_HANDLED;

}

void print_message(DBusMessage *message) {
    DBusMessageIter iter; //参数/返回值迭代器
    const char *sender;
    const char *destination;
    int message_type;
    struct timeval t;
    gettimeofday(&t, NULL);
    message_type = dbus_message_get_type (message);
    sender = dbus_message_get_sender (message);
    destination = dbus_message_get_destination (message);
    printf("%s time=%ld.%06ld sender=%s -> destination=%s",
           msg_type2str[message_type],
           t.tv_sec, t.tv_usec,
           sender ?sender: "(null sender)",
           destination ?destination: "(null destination)");
    switch (message_type) {
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
    case DBUS_MESSAGE_TYPE_SIGNAL:
        printf (" serial=%u path=%s; interface=%s; member=%s\n",
                dbus_message_get_serial (message),
                dbus_message_get_path (message),
                dbus_message_get_interface (message),
                dbus_message_get_member (message));
        break;
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
        printf (" serial=%u reply_serial=%u\n",
                dbus_message_get_serial (message),
                dbus_message_get_reply_serial (message));
        break;
    case DBUS_MESSAGE_TYPE_ERROR:
        printf (" error_name=%s reply_serial=%u\n",
                dbus_message_get_error_name (message),
                dbus_message_get_reply_serial (message));
        break;
    default:
        printf ("\n");
        break;
    }
    dbus_message_iter_init (message, &iter);
    walk_iter(&iter, 1);
    fflush (stdout);
}

void walk_iter(DBusMessageIter *iter, int depth) {
    while(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
        print_iter(iter, depth);
        dbus_message_iter_next (iter);
    }
}

void print_iter(DBusMessageIter *iter, int depth) {
    char *vstr;
    dbus_int16_t vi16;
    dbus_uint16_t vu16;
    dbus_int32_t vi32;
    dbus_uint32_t vu32;
    dbus_int64_t vi64;
    dbus_uint64_t vu64;
    double vdf;
    unsigned char vuc;
    dbus_bool_t vbool;
    DBusMessageIter vsubiter;
    int vfd;
    int current_type;
    int type = dbus_message_iter_get_arg_type (iter);
    printf("%*s", depth*INDENT, "");
    switch (type) {
    case DBUS_TYPE_STRING:
        dbus_message_iter_get_basic (iter, &vstr);
        printf ("string \"%s\"\n", vstr);
        break;
    case DBUS_TYPE_SIGNATURE:
        dbus_message_iter_get_basic (iter, &vstr);
        printf ("signature \"%s\"\n", vstr);
        break;
    case DBUS_TYPE_OBJECT_PATH:
        dbus_message_iter_get_basic (iter, &vstr);
        printf ("object path \"%s\"\n", vstr);
        break;
    case DBUS_TYPE_INT16:
        dbus_message_iter_get_basic (iter, &vi16);
        printf ("int16 %d\n", vi16);
        break;
    case DBUS_TYPE_UINT16:
        dbus_message_iter_get_basic (iter, &vu16);
        printf ("uint16 %u\n", vu16);
        break;
    case DBUS_TYPE_INT32:
        dbus_message_iter_get_basic (iter, &vi32);
        printf ("int32 %d\n", vi32);
        break;
    case DBUS_TYPE_UINT32:
        dbus_message_iter_get_basic (iter, &vu32);
        printf ("uint32 %u\n", vu32);
        break;
    case DBUS_TYPE_INT64:
        dbus_message_iter_get_basic (iter, &vi64);
        printf ("int64 %ld\n", vi64);
        break;
    case DBUS_TYPE_UINT64:
        dbus_message_iter_get_basic (iter, &vu64);
        printf ("uint64 %lu\n", vu64);
        break;
    case DBUS_TYPE_DOUBLE:
        dbus_message_iter_get_basic (iter, &vdf);
        printf ("double %g\n", vdf);
        break;
    case DBUS_TYPE_BYTE:
        dbus_message_iter_get_basic (iter, &vuc);
        printf ("byte %d\n", vuc);
        break;
    case DBUS_TYPE_BOOLEAN:
        dbus_message_iter_get_basic (iter, &vbool);
        printf ("boolean %s\n", vbool ? "TRUE" : "FALSE");
        break;
    case DBUS_TYPE_VARIANT:
        //vsubiter此时代表variant包裹的一个数据(可以是任意类型)
        dbus_message_iter_recurse (iter, &vsubiter);
        printf ("variant\n");
        walk_iter(&vsubiter, depth+1);
        break;
    case DBUS_TYPE_ARRAY:
        //vsubiter此时代表arry元素(任意类型)的迭代器
        dbus_message_iter_recurse (iter, &vsubiter);
        //字节码数组特殊处理
        current_type = dbus_message_iter_get_arg_type (&vsubiter);
        if (current_type == DBUS_TYPE_BYTE) {
            print_ay(&vsubiter, depth);
            break;
        }
        printf("array [\n");
        walk_iter(&vsubiter, depth+1);
        printf("%*s]\n", depth*INDENT, "");
        break;
    case DBUS_TYPE_DICT_ENTRY:
        printf("dict entry(\n");
        //vsubiter此时为一个(键,值)对的迭代器
        dbus_message_iter_recurse (iter, &vsubiter);
        walk_iter(&vsubiter, depth+1);
        printf("%*s)\n", depth*INDENT, "");
        break;
    case DBUS_TYPE_STRUCT:
        printf("struct {\n");
        //vsbuiter:结构体元素迭代器
        dbus_message_iter_recurse (iter, &vsubiter);
        walk_iter(&vsubiter, depth+1);
        printf("%*s}\n", depth*INDENT, "");
        break;
    case DBUS_TYPE_UNIX_FD:
        dbus_message_iter_get_basic (iter, &vfd);
        print_fd (vfd, depth+1);
        /* dbus_message_iter_get_basic() duplicated the fd, we need to
               * close it after use. The original fd will be closed when the
               * DBusMessage is released.
               */
        close (vfd);
        break;

    default:
        printf (" (dbus-monitor too dumb to decipher arg type '%c')\n", type);
        break;
    }
}

void print_ay(DBusMessageIter *iter, int depth) {
    dbus_bool_t all_ascii = TRUE;
    const unsigned char *bytes;
    int len;
    int i;
    dbus_message_iter_get_fixed_array (iter, &bytes, &len);
    for (i = 0; i < len; i++){
        if ((bytes[i] < 32 || bytes[i] > 126) &&
                (i < len - 1 || bytes[i] != '\0')) {
            all_ascii = FALSE;
            break;
        }
    }
    if (all_ascii && len > 0 && bytes[len - 1] == '\0') {
        printf ("array of bytes \"%s\" + \\0\n", bytes);
    } else if (all_ascii) {
        unsigned char *copy = (unsigned char *)dbus_malloc (len + 1);
        if (copy == NULL) {
            fprintf(stderr, "OOM while %s\n", "copying bytestring");
            return;
        }
        memcpy (copy, bytes, len);
        copy[len] = '\0';
        printf ("array of bytes \"%s\"\n", copy);
        dbus_free (copy);
    }else {
        print_hex (bytes, len, depth);
    }

}

void print_hex(const unsigned char *bytes, unsigned len, int depth) {
    unsigned int i, columns;
    printf ("array of bytes [\n");
    printf("%*s", (depth + 1)*4, "");
    /* Each byte takes 3 cells (two hexits, and a space), except the last one. */
    columns = (80 - ((depth + 1) * INDENT)) / 3;
    if (columns < 8) columns = 8;
    i = 0;
    while (i < len) {
        printf ("%02x", bytes[i]);
        i++;
        if (i != len) {
            if (i % columns == 0) {
                printf ("\n");
                printf("%*s", (depth + 1)*4, "");
            } else {
                printf (" ");
            }
        }
    }
    printf ("\n%*s]\n", depth*4, "");
}


void print_fd (int fd, int depth) {
    int ret;
    struct stat statbuf = {0,};
    printf ("file descriptor %d\n", fd);
    if (fd == -1) return;
    ret = fstat (fd, &statbuf);
    if (ret == -1) return;
    printf("%*s", (depth+1)*4, "");
    printf ("inode: %d\n", (int) statbuf.st_ino);
    printf("%*s", (depth+1)*4, "");
    printf ("type: ");
    switch (statbuf.st_mode & S_IFMT) {
    case S_IFREG: printf ("file\n"); break;
    case S_IFDIR: printf ("directory\n"); break;
    case S_IFCHR: printf ("char\n"); break;
    case S_IFBLK: printf ("block\n"); break;
    case S_IFIFO: printf ("fifo\n"); break;
    case S_IFLNK: printf ("link\n"); break;
    case S_IFSOCK: printf ("socket\n"); break;
    default: printf("unknown?\n");  break;
    }

}



